package com.fruit.logistics.yunda.service;

import com.fruit.logistics.AbstractLogisticsProxyService;
import com.fruit.logistics.yunda.pojo.YDResponse;
import com.fruit.logistics.yunda.pojo.order.Item;
import com.fruit.logistics.yunda.pojo.order.UserInfo;
import com.fruit.logistics.yunda.pojo.order.YDOrder;
import com.fruit.logistics.yunda.pojo.order.YDOrderResponse;
import com.fruit.logistics.yunda.pojo.route.YDRouteResponse;
import com.fruit.pojo.logistics.*;
import com.fruit.service.logistics.LogisticsService;
import com.fruit.service.logistics.NoSuitableProxyException;
import com.joe.http.IHttpClientUtil;
import com.joe.http.request.IHttpRequestBase;
import com.joe.utils.common.FormDataBuilder;
import com.joe.utils.common.StringUtils;
import com.joe.utils.parse.json.JsonParser;
import com.joe.utils.parse.xml.XmlParser;
import com.joe.utils.secure.IBase64;
import com.joe.utils.secure.MD5;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * 韵达快递代理
 *
 * @author joe
 * @version 2018.05.14 10:53
 */
@Slf4j
@Service
public class YDLogisticsProxyService extends AbstractLogisticsProxyService {
    private static final MD5 MD5 = new MD5();
    private static final IBase64 BASE64 = new IBase64();
    private static final XmlParser XML_PARSER = XmlParser.getInstance();
    private static final JsonParser JSON_PARSER = JsonParser.getInstance();
    private static final IHttpClientUtil CLIENT = new IHttpClientUtil();
    private static final String XML_FORMAT = "<orders>%s</orders>";
    /**
     * 快递查询接口格式化字符串，需要提供一个String类型的订单号参数
     */
    private static final String SEARCH_FORMAT =
            "<orders><order><order_serial_no>%s</order_serial_no><mailno></mailno><print_file>0</print_file" +
                    "><json_data>1</json_data></order></orders>";

    /**
     * 接口地址
     */
    @Value("${com.fruit.logistics.yd.url}")
    private String url;
    /**
     * 路由URL
     */
    @Value("${com.fruit.logistics.yd.route.url}")
    private String routeUrl;
    /**
     * 带参数的路由URL，初始化时赋值
     */
    private String routeUrlWithParam;
    /**
     * 韵达合作商ID
     */
    @Value("${com.fruit.logistics.yd.partnerid}")
    private String partnerid;
    /**
     * 韵达提供的密码
     */
    @Value("${com.fruit.logistics.yd.password}")
    private String password;

    @Override
    public void init(LogisticsService service) {
        super.init(service);
        routeUrlWithParam = String.format(routeUrl + "?partnerid=%s&mailno=", partnerid) + "%s";
    }

    @Override
    public LogisticsType getProxyType() {
        return LogisticsType.YD;
    }

    @Override
    public SysRoute routeNotify(String data) throws NoSuitableProxyException {
        return null;
    }

    @Override
    public SysLogisticsOrderResponse send(SysLogistics logistics) throws NoSuitableProxyException {
        if (logistics == null) {
            throw new NullPointerException("韵达订单发送参数不能为空");
        }
        log.info("要发送的订单信息为：[{}]", logistics);
        YDOrder order = convertToOrder(logistics);
        log.debug("要发送的韵达订单为：[{}]", order);
        SysLogisticsOrderResponse response = send(url + "interface_receive_order__mailno.php", order, true, true,
                "data",
                YDOrderResponse.class,
                new SysLogisticsOrderResponse(), build());
        log.info("订单信息[{}]对应的订单系统结果为：[{}]", logistics, response);
        return response;
    }

    @Override
    public SysLogisticsOrderResponse searchSend(String orderId) {
        if (StringUtils.isEmpty(orderId)) {
            throw new NullPointerException("订单ID不能为空");
        }
        String data = String.format(SEARCH_FORMAT, orderId);


        log.debug("要查询的韵达订单为：[{}]", orderId);
        SysLogisticsOrderResponse response = send(url + "interface_order_info.php", data, true, true,
                "data",
                YDOrderResponse.class,
                new SysLogisticsOrderResponse(), build());
        log.info("订单信息[{}]对应的订单系统结果为：[{}]", orderId, response);
        return response;
    }

    @Override
    public SysRouteResponse searchRoute(String id, boolean isSys) {
        if (StringUtils.isEmpty(id)) {
            throw new NullPointerException("查询路由的订单ID不能为空");
        }
        if (isSys) {
            throw new NoSuitableProxyException("韵达快递暂时不支持使用系统订单ID查询");
        }


        SysRouteResponse response = send(String.format(routeUrlWithParam, id), null, false, false,
                "data", YDRouteResponse.class, new SysRouteResponse(), (resp, sysResp) -> {
                    sysResp.setSuccess(resp.isResult());
                    sysResp.setMailno(resp.getMailno());
                    sysResp.setErrorCode(resp.getRemark());
                    if (resp.getSteps() != null && !resp.getSteps().isEmpty()) {
                        sysResp.setRoutes(resp.getSteps().stream().map(trace -> {
                            SysRoute route = new SysRoute();
                            route.setAcceptTime(trace.getTime());
                            route.setAcceptAddress(trace.getAddress());
                            route.setRemark(trace.getRemark());
                            route.setMailno(resp.getMailno());
                            route.setOpcode(trace.getStatus());
                            return route;
                        }).collect(Collectors.toList()));
                    }
                    return sysResp;
                });
        return response;
    }

    @Override
    public SysDeliverResponse searchDeliver(SysDeliver deliver) {
        throw new NoSuitableProxyException("韵达快递没有时效查询接口");
    }

    @Override
    public SysNewDeliverResponse searchDeliver(SysNewDeliver deliver) {
        throw new NoSuitableProxyException("韵达快递没有时效查询接口");
    }


    /**
     * 构建订单转换函数，创建订单和查询订单使用
     *
     * @return 订单转换函数
     */
    private BiFunction<YDOrderResponse, SysLogisticsOrderResponse, SysLogisticsOrderResponse> build() {
        return (ydResp, sysResp) -> {
            sysResp.setDuplicate(false);
            sysResp.setPickup(false);
            sysResp.setNotify(false);
            sysResp.setBack(false);
            sysResp.setMailno(ydResp.getMailNo());
            sysResp.setOrderid(ydResp.getOrderSerialNo());
            if (!StringUtils.isEmpty(ydResp.getJsonData())) {
                sysResp.setReturnTrackingNo(ydResp.getJsonData());
            } else if (!StringUtils.isEmpty(ydResp.getPdfInfo())) {
                sysResp.setReturnTrackingNo(ydResp.getPdfInfo());
            }
            return sysResp;
        };
    }

    /**
     * 发送韵达请求
     *
     * @param url        请求的URL
     * @param dataObj    请求数据
     * @param request    请求request类型
     * @param resultType 请求结果类型Class
     * @param sysResult  系统请求结果对象
     * @param converter  转换器（用于请求结束后将韵达请求结果转换为系统请求结果）
     * @param <T>        结果类型
     * @param <R>        系统结果类型
     * @return 请求结果（系统结果）
     */
    private <T extends YDResponse, R extends LogisticsResponse> R send(String url, Object dataObj, boolean xml,
                                                                       boolean post,
                                                                       String request,
                                                                       Class<T> resultType, R sysResult,
                                                                       BiFunction<T, R, R> converter) {
        log.debug("准备发起请求，请求数据为：[{}]，request为：[{}]，resulttype为：[{}]", dataObj, request, resultType);
        try {
            String resultStr;
            if (post) {
                String dataStr = convert(dataObj, request);
                resultStr = post(url, dataStr);
            } else {
                resultStr = get(url);
            }
            log.info("韵达发送请求结果为：[{}]", resultStr);
            resultStr = resultStr.replace("<responses>", "").replace("</responses>", "");

            T result;

            if (xml) {
                result = XML_PARSER.parse(resultStr, resultType);
            } else {
                result = JSON_PARSER.readAsObject(resultStr, resultType);
            }

            log.info("韵达请求结果解析结果为：[{}]", result);

            if (result.getCode() != 1) {
                sysResult.setSuccess(false);
                sysResult.setErrorCode(result.getMsg());
            }

            sysResult = converter.apply(result, sysResult);
        } catch (Exception e) {
            log.error("韵达请求发送失败，请求数据为：[{}]，request为：[{}]，resulttype为：[{}]", dataObj, request, resultType, e);
            sysResult.setE(e);
            sysResult.setSuccess(false);
        }
        sysResult.setType(LogisticsType.YD);
        return sysResult;
    }

    private String post(String url, String data) throws IOException {
        return CLIENT.executePost(url, data, "UTF8", "UTF8", IHttpRequestBase.CONTENT_TYPE_FORM);
    }

    private String get(String url) throws IOException {
        return CLIENT.executeGet(url);
    }

    /**
     * 将系统订单转换为韵达订单
     *
     * @param logistics 系统订单
     * @return 韵达订单
     */
    private YDOrder convertToOrder(SysLogistics logistics) {
        log.debug("将系统订单[{}]转换为韵达订单", logistics);
        YDOrder order = new YDOrder();

        order.setOrderSerialNo(logistics.getOrderid());
        order.setKhddh(logistics.getOrderid());

        //发件人信息
        UserInfo sender = new UserInfo();
        sender.setName(logistics.getJContact());
        sender.setCity(connect(logistics.getJProvince(), logistics.getJCity(), logistics.getJCounty()));
        sender.setCompany(logistics.getJCompany());
        sender.setAddress(logistics.getJAddress());
        sender.setPhone(logistics.getJTel());
        sender.setPostcode(logistics.getJPostCode());
        order.setSender(sender);

        //收件人信息
        UserInfo receiver = new UserInfo();
        receiver.setName(logistics.getDContact());
        receiver.setCity(connect(logistics.getDProvince(), logistics.getDCity(), logistics.getDCounty()));
        receiver.setCompany(logistics.getDCompany());
        receiver.setAddress(logistics.getDAddress());
        receiver.setPhone(logistics.getDTel());
        receiver.setPostcode(logistics.getDPostCode());
        order.setReceiver(receiver);


        //商品信息
        SysCargo cargo = logistics.getCargo();
        if (cargo != null) {
            Item item = new Item();
            item.setName(cargo.getName());
            item.setNumber(cargo.getCount());
            order.setItems(Collections.singletonList(item));
        }

        log.debug("系统订单[{}]转换为韵达订单后是：[{}]", logistics, order);
        return order;
    }

    /**
     * 连接省市区
     *
     * @param province 省
     * @param city     市
     * @param county   区
     * @return 连接结果
     */
    private String connect(String province, String city, String county) {
        List<String> list = new ArrayList<>(3);
        if (province != null) {
            list.add(province);
        }
        if (city != null) {
            list.add(city);
        }
        if (county != null) {
            list.add(county);
        }

        return String.join(",", list);
    }

    /**
     * 将原始xml数据转换为要发送的表单数据
     *
     * @param xmlObj  原始数据
     * @param request 数据请求类型
     * @return 要发送的表单数据
     */
    private String convert(Object xmlObj, String request) {
        log.debug("将[{}]转换为xml", xmlObj);
        if (xmlObj == null) {
            return null;
        }

        String xml;

        if (xmlObj instanceof String) {
            xml= (String) xmlObj;
        }else{
            xml = XML_PARSER.toXml(xmlObj, "order", false);
            xml = String.format(XML_FORMAT, xml);
        }
        log.debug("[{}]转换为xml为：[{}]", xmlObj, xml);
        String validation = sign(xml);
        try {
            xml = URLEncoder.encode(BASE64.encrypt(xml), "UTF8");
        } catch (Exception e) {
            log.error("编码异常", e);
        }
        return FormDataBuilder.builder().form("partnerid", partnerid).form("version", "1.0").form("request", request)
                .form("xmldata", xml).form("validation", validation).data();
    }

    /**
     * 签名
     *
     * @param xmlData 原始xml数据
     * @return 签名数据
     */
    private String sign(String xmlData) {
        String baseXml = BASE64.encrypt(xmlData);
        String signData = baseXml + partnerid + password;
        return MD5.encrypt(signData);
    }

    public static void main(String[] args) throws Exception {
        YDOrder order = new YDOrder();
        Item item = new Item();
        item.setRemark("备注");
        item.setNumber(12);
        item.setName("名字");
        List<Item> list = new ArrayList<>();
        list.add(item);
        list.add(item);
        order.setItems(list);
        System.out.println(XML_PARSER.toXml(order));
        System.exit(0);


        String xml = "<order></order>";
        String reqeust = "data";
        YDLogisticsProxyService service = new YDLogisticsProxyService();
        service.password = "123456";
        service.partnerid = "YUNDA";
        System.out.println(service.convert(xml, reqeust));
    }
}
